/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package db.connectionPool;


import db.configuration.FileWatcher;
import db.configuration.ConfigAdmin;
import db.configuration.ServerActiveException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Observable;
import java.util.Observer;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;





/**
 *
 * Clase que proporciona las conexiones a la base datos.
 */


public class DbConnectionPool implements Observer{
     private int sizePool;
     private Stack <DbConnection> AvailableConnections;
     private Stack <DbConnection> NoActualBusyConnections;
     private Stack <DbConnection> ActualBusyConnections;
     private DBConfiguration currentDbConfig;
     private boolean isAvailable;
     private FileWatcher watcher;
     private ConfigAdmin configAdmin;


     /**
      *
      * @throws SQLException
      * @throws IOException
      * @throws Exception
      */
     public DbConnectionPool() throws SQLException, IOException, Exception{

            configAdmin = new ConfigAdmin();
            currentDbConfig = new DBConfiguration();
            loadDBConfiguration();
            sizePool = configAdmin.getConnectionSize();
            AvailableConnections = new Stack<DbConnection>();
            NoActualBusyConnections = new Stack<DbConnection>();
            ActualBusyConnections = new Stack<DbConnection>();
            this.createDbConnections();
            this.openDBConnections();
            this.isAvailable = true;
            configWatcher();
    }

    /**
    * Crea las conexiones a la base de datos.
    * @throws SQLException
     * @throws Exception
    */
     private void createDbConnections() throws SQLException, Exception{
        for(int i=0; i<sizePool; i++){
            AvailableConnections.push(DbConnection.initConnection());
        }
     }

     /**
      * Abre las conexiones a la base de datos.
      * @throws SQLException
      * @throws Exception
      */
     private void openDBConnections()throws SQLException, Exception{
        for(int i=0; i<AvailableConnections.size(); i++){
             AvailableConnections.elementAt(i).openConection(currentDbConfig);
        }
     }
     /**
      * Cierra las conexiones a la base de datos.
      * @throws Exception
      */

     private void closeDbConnections() throws Exception{
        for(int i=0; i<AvailableConnections.size(); i++){
            AvailableConnections.elementAt(i).closeConection();
        }
     }

     /**
      * Proporciona una conexion ya establecida a la base de datos.
      * @return DbConnection
      */
     public DbConnection getDbConnection() throws Exception{
            if(AvailableConnections.size() != 0){
                ActualBusyConnections.push(AvailableConnections.pop());
                return  ActualBusyConnections.lastElement();
            }else{
                this.reSizePool(this.sizePool+5);
                ActualBusyConnections.push(AvailableConnections.pop());
                return  ActualBusyConnections.lastElement();
            }
     }

     /**
      * Reajusta el tamaño del pool de conexiones disponibles.
      * Cuando se requieran mas o menos de los que existen.
      * @param size
      * @throws Exception
      */
     public void reSizePool(int size) throws Exception{
        int numAvailableConnection = size-(ActualBusyConnections.size()+NoActualBusyConnections.size());
        if(numAvailableConnection>0){
            if(numAvailableConnection>AvailableConnections.size()){
                for(int i = 0; i<(numAvailableConnection-AvailableConnections.size()); i++){
                    AvailableConnections.push(DbConnection.initConnection());
                    AvailableConnections.lastElement().openConection(currentDbConfig);
                }
            }
            else{
                for(int i = 0; i<(numAvailableConnection-AvailableConnections.size()); i++){
                    AvailableConnections.pop();
                }
            }

        }

     }


     /**
      * Regresa una conexion que ya fue utilizada a la pila de las conexiones
      * disponibles.
      * @param connection
      * @throws Exception
      */
     public void returnDbConnection(DbConnection connection) throws Exception{
        for(int i = 0; i<ActualBusyConnections.size(); i++){
            if(ActualBusyConnections.elementAt(i).equals(connection)){
                AvailableConnections.push(ActualBusyConnections.get(i));
                ActualBusyConnections.remove(i);
            }
        }

        for(int j = 0; j<NoActualBusyConnections.size(); j++){
            if(NoActualBusyConnections.elementAt(j).equals(connection)){
                NoActualBusyConnections.elementAt(j).closeConection();
                NoActualBusyConnections.elementAt(j).openConection(currentDbConfig);
                AvailableConnections.push(NoActualBusyConnections.get(j));
                NoActualBusyConnections.remove(j);
            }
        }

     }

     /**
      * Mueve todas las conexiones de la pila de conexiones actualizadas a
      * las no actualizadas.
      */
     private void moveDbConnection(){
        for(int i = 0; i<ActualBusyConnections.size(); i++){
            NoActualBusyConnections.push(ActualBusyConnections.pop());
        }
    }

    /**
     * Cierra las conexiones de la pila de conexiones disponibles
     * y las vuelve a abrir.
     * @throws Exception
     */
     private void refreshDbConnections() throws Exception{
         this.closeDbConnections();
         this.openDBConnections();
     }

     /**
      * Carga la configuracion para la conexion a la base de datos.
      */
     private void loadDBConfiguration() throws ServerActiveException{
        configAdmin.loadConfiguration();
        this.currentDbConfig= configAdmin.getConfiguration(); // DB = MySQL no debe pasar nada.. soluciona alex y nos avisa..... para actualizar...
     }


     /**
      * Verifica si el archivo de configuracion cambio, si el archivo fue modificada
      * carga esa nueva configuracion.
      */
     private void configWatcher(){
        String dbConfigurationFile = configAdmin.getFileName();// archivo q tiene yichao...
	try {
            watcher = new FileWatcher(dbConfigurationFile,5);
	    watcher.addObserver(this);
	    new Thread(watcher).start();

        } catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
	}

     }


     /**
      *
      * @return
      */
     public boolean isAvailable(){
        return this.isAvailable;
    }

    /**
     *
     */
    public void changeAvailability(boolean poolState){
        isAvailable= poolState;
    }

    /**
     * Cuando el archivo de configuracion ha sido modificado hace lo siguiente.
     *  -Carga la nueva configuracion.
     *  -Mueve todas las conexiones de la pila de conexiones actualizadas a la
     *   pila de conexiones no actualizadas,
     *  -Cierra las conexiones de la pila de conexiones disponibles y las vuelve
     *   a abrir con la nueva configuracion.
     * @param o
     * @param arg
     */
    public void update(Observable o, Object arg) {
        try {
            this.loadDBConfiguration();
            this.moveDbConnection();
            this.refreshDbConnections();
        } catch (Exception ex) {
            Logger.getLogger(DbConnectionPool.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

}


    

